/*
 *  Copyright (C) 2007 by Filip Brčić <brcha@users.sourceforge.net>
 *
 *  This file is part of OOMTK (http://launchpad.net/oomtk)
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/** \file IDT.cc
 * \brief Description
 */
#include "IDT.h"

#include "Selectors.h"

#include <types.h>

#include <stdio.h>
#include <ansi.h>
#include <string.h>

#include <PortIO.h>

IDT * IDT::instance()
{
  static IDT instance = IDT();

  return &instance;
};

// Make the interrupt descriptor table
// TODO: NUM_IRQ should be defined elsewhere, probably
#define NUM_IRQ 256
PAGE_ALIGN static uint64_t RealIDT[NUM_IRQ];
PAGE_ALIGN static uint64_t RealIDTD;

IDT::IDT()
{
  IdtTable = reinterpret_cast<GateDescriptor_t*>(&RealIDT);
  IdtDescriptor = reinterpret_cast<uint32_t*>(&RealIDTD);
}

// This is generated by python from IdtDeclaration.py:
//// BEGIN INSERTED CODE ////
#include "__generatedIdtDeclarationCode__.h"
//// END INSERTED CODE ////

// This function does the true work
void IDT::setup()
{
  printf("Loading IDT ... ");
  // initialize interrupt controller (?)

  // Initialize the IdtTable (to 0)
  for(int i=0; i < NUM_IRQ; i++)
    RealIDT[i] = 0;

  // Initialize the entries
  // This is generated by python from IdtInitialization.py:
  //// BEGIN INSERTED CODE ////
  # include "__generatedIdtInitializationCode__.cc"
  //memset(&IdtTable, 0, sizeof(GateDescriptor_t)*NUM_IRQ);
  //// END INSERTED CODE ////

  // Set all interrupt handlers to point to some stub (or do that in constructor)
  // And of course those that do point somethere to wherever they point to...

  // Try to setup the descriptor...
  uint32_t widt = reinterpret_cast<uint32_t>(&IdtTable);
  IdtDescriptor[0] = (NUM_IRQ*8-1) | ((widt & 0xffff) << 16);
  IdtDescriptor[1] = widt >> 16;

  /* Remap PICs to 31-47 */
  PortIO::out8(0x20, 0x11);
  PortIO::out8(0xA0, 0x11);
  PortIO::out8(0x21, 0x20);
  PortIO::out8(0xA1, 0x28);
  PortIO::out8(0x21, 0x04);
  PortIO::out8(0xA1, 0x02);
  PortIO::out8(0x21, 0x01);
  PortIO::out8(0xA1, 0x01);
  PortIO::out8(0x21, 0x0);
  PortIO::out8(0xA1, 0x0);

  GNU_ASM(
	  "   lidt  0(%0)" // load IDTD
          : /* no output */
	  : "r" ((void*)&RealIDTD)
	  );
  printf(ANSI_FG_GREEN "[ok]\n" ANSI_NORMAL);

  printf("&RealIDTD = 0x%08x\n", &RealIDTD);
  printf("widt = %08x\n", widt);
  printf("&IdtTable = %08x\n", &IdtTable);
  printf("IdtDescriptor = 0x%08x,0x%08x\n", IdtDescriptor[1], IdtDescriptor[0]);
  printf("sizeof(GateDescriptor_t) = 0x%08x\n", sizeof(GateDescriptor_t));

//   asm("sti\n");
//   asm("int $49\n");
}

void IDT::SetHardwareVector(int entry, void (*handlerPtr)(void), bool user)
{
  uint32_t intHandlerPtr = reinterpret_cast<uint32_t>(handlerPtr);

  IdtTable[entry].bits.offset_lo  = (uint16_t) intHandlerPtr;
  IdtTable[entry].bits.selector   = sel_KernelCS;
  IdtTable[entry].bits.zero       = 0;
  IdtTable[entry].bits.type       = 0xeu;
  IdtTable[entry].bits.system     = 0;
  /* Use RPL==1 for non-user so kernel threads can call them. */
  IdtTable[entry].bits.dpl        = user ? 3 : 1;
  IdtTable[entry].bits.present    = 1;
  IdtTable[entry].bits.offset_hi  = (uint16_t) (intHandlerPtr >> 16);
}

